package ru.aslanov.gcal_connect;

import com.google.gdata.client.calendar.CalendarService;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.XmlEventSource;
import com.google.gdata.data.calendar.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import ru.aslanov.schedule.server.gcalendar.GCalendarConnectService;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by IntelliJ IDEA.
 * Created: Apr 6, 2009 4:20:53 PM
 *
 * @author Sergey Aslanov
 */
public class GCalConnect {
    private static final Logger log = Logger.getLogger(GCalConnect.class.getName());
    private CalendarService calendarService;
    private CalendarFeed calendarFeed;
    private static final URL CALENDARS_URL;

    static {
        try {
            CALENDARS_URL = new URL("http://www.google.com/calendar/feeds/default/owncalendars/full");
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    private static final String[] calendars = {
            // Boogie
            "o6h7rqsm3tbjl6p3ngpalec594@group.calendar.google.com",
            "c9rieu06nim91f4ghi2uapp0kg@group.calendar.google.com",
            "ido5usodf10cucjeegb0r4r5h4@group.calendar.google.com",
            "qtaja164d0qugfphbrpnfganrk@group.calendar.google.com",
            "3bg167jsgam5u484r1l8ibv484@group.calendar.google.com",
            "8pivreplpmof096orjagec11vc@group.calendar.google.com",
            "fi7tfdiai5nj3c6me97rofm6n8@group.calendar.google.com",
            //Lindy
            "ggef7nbu26ofg9np926o7jj814@group.calendar.google.com",
            "2ecbkp6nj4bqcc0kstjbh5dii4@group.calendar.google.com",
            "hbstgt04ai77cu70q2lhhh3pp8@group.calendar.google.com",
            "se1oml9h2b4tpdvuvu1k5vhhl8@group.calendar.google.com",
            //Other
            "59gv0bpbk35p7b8r9fq3q519k4@group.calendar.google.com",
            "1pvfsvctfsscd8k4c5sk0kvmvc@group.calendar.google.com",
            "s9p6uuhrh8tn81phfvr3kgud5s@group.calendar.google.com",
            "rq5u3bgu12q6f5u183iviv37n4@group.calendar.google.com",
            "v7ot9ej0fisf6f4uctrg0cnlco@group.calendar.google.com",
            //Privates
            "0fsv7mp3h6b280dv0f5eugp3b0@group.calendar.google.com"
    };


    public static void main(String[] args) throws Exception {
        setupProxy();

        GCalConnect gCalConnect = new GCalConnect();

        //gCalConnect.sync();

        File outDir = new File("out/from_gcal");
        outDir.mkdirs();
        for (String calendarId : calendars) {
            gCalConnect.loadEventsToFile(calendarId, new File(outDir, calendarId + ".xml"));
        }
    }


    public GCalConnect() throws Exception {
        calendarService = new CalendarService("schedule");
        //calendarService.setUserCredentials("saslanov2@i.ua", ",ekmljpth");
    }

    private void sync() throws Exception {
        log.info("Loading calendars");
        calendarFeed = calendarService.getFeed(CALENDARS_URL, CalendarFeed.class);

        DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document = documentBuilder.parse(new File("out/schedule_gcal.xml"));

        Element rootEl = document.getDocumentElement();

        for (Element calEl : getChildElements(rootEl, "calendar")) {
            String name = calEl.getAttribute("name");

            CalendarEntry calendarEntry = prepareCalendar(name);

            for (final Element entryEl : getChildElements(calEl, "entry")) {
                CalendarEventEntry calendarEventEntry = new CalendarEventEntry();
                ExtensionProfile extProfile = new ExtensionProfile();
                extProfile.addDeclarations(calendarEventEntry);
                calendarEventEntry.parseAtom(extProfile, new MyXmlEventSource(entryEl));

                createEvent(calendarEntry, calendarEventEntry);
            }
        }

        //calendarService.in
    }

    private void loadEventsToFile(String calendarId, File toFile) throws Exception {
        try {
            final Document document = GCalendarConnectService.loadEvents(calendarId);

            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.transform(new DOMSource(document), new StreamResult(toFile));

        } catch (Exception e) {
            log.log(Level.WARNING, "", e);
        }
    }


    private void createEvent(CalendarEntry calendarEntry, CalendarEventEntry calendarEventEntry) throws Exception {
        log.info("Creating event " + calendarEventEntry.getTitle().getPlainText());

        URL feedUrl = getCalendarEventsURL(calendarEntry);
        CalendarEventEntry newEventEntry = calendarService.insert(feedUrl, calendarEventEntry);
        log.info("Event created: " + newEventEntry.getId());
    }

    private URL getCalendarEventsURL(CalendarEntry calendarEntry) throws MalformedURLException {
        String calId = calendarEntry.getId();
        URL feedUrl = new URL("http://www.google.com/calendar/feeds/" + calId.substring(calId.lastIndexOf('/') + 1) + "/private/full");
        return feedUrl;
    }

    private CalendarEntry prepareCalendar(String name) throws Exception {
        CalendarEntry entry = null;
        for (CalendarEntry calendarEntry : calendarFeed.getEntries()) {
            if (name.equals(calendarEntry.getTitle().getPlainText())) {
                if (entry != null) {
                    throw new RuntimeException("Duplicate calendar with name '" + name + "'");
                }
                log.info("Calendar '" + name + "' matched: " + calendarEntry.getId());
                entry = calendarEntry;
            }
        }

        if (entry == null) {
            log.info("Creating calendar '" + name + "'");
            entry = new CalendarEntry();
            entry.setTitle(new PlainTextConstruct(name));
            entry.setHidden(HiddenProperty.FALSE);

            entry = calendarService.insert(CALENDARS_URL, entry);
            log.info("Calendar created, id=" + entry.getId());
        } else {
            clearCalendar(entry);            
        }
        return entry;

    }

    private void clearCalendar(CalendarEntry entry) throws Exception {
        log.info("Removing all events from calendar: " + entry.getTitle().getPlainText());

        CalendarEventFeed feed = calendarService.getFeed(getCalendarEventsURL(entry), CalendarEventFeed.class);
        for (CalendarEventEntry calendarEventEntry : feed.getEntries()) {
            calendarEventEntry.delete();
        }

    }

    private static List<Element> getChildElements(Node elem, String name) {
        NodeList list = elem.getChildNodes();
        List<Element> res = new ArrayList<Element>();
        for (int i = 0; i < list.getLength(); i++) {
            Node node = list.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE && name.equals(((Element)node).getTagName())) {
                res.add((Element) node);
            }
        }
        return res;
    }

    private static void setupProxy() {
        final Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("10.140.192.81", 8080));

        ProxySelector.setDefault(new ProxySelector() {
            public List<Proxy> select(URI uri) {
                return Collections.singletonList(proxy);
            }
            public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
                System.err.println("Connection failed: " + uri + "\nsocket: " + sa);
                ioe.printStackTrace();
            }
        });
    }

    private static class MyXmlEventSource implements XmlEventSource {
        private final Element entryEl;
        private static Transformer TRANSFORMER;
        static {
            try {
                TRANSFORMER = TransformerFactory.newInstance().newTransformer();
            } catch (TransformerConfigurationException e) {
                throw new RuntimeException(e);
            }
        }

        public MyXmlEventSource(Element entryEl) {
            this.entryEl = entryEl;
        }

        public void parse(DefaultHandler handler) throws SAXException {
            try {
                TRANSFORMER.transform(new DOMSource(entryEl), new SAXResult(handler));
            } catch (TransformerException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
